#include "ftp_server.h"


#include "tcp_server.h"

#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>

#include "unistd.h"
#include "esp_vfs.h"
#include "esp_system.h"

#define PORT                        21
#define KEEPALIVE_IDLE              6
#define KEEPALIVE_INTERVAL          5
#define KEEPALIVE_COUNT             5

static int processSock=0;

static const char *TAG = "tcp-server";
static const char* PATH="/spiffs";

extern uint32_t UserIP;

static void sendData(char*data,int len){
    send(processSock,data,len,0);
}

static void closeSock(){
    shutdown(processSock, 0);
    close(processSock);
}
static int getFileSize(char*str){
    char*buffs=(char*)malloc(300);
    sprintf(buffs,"%s%s",PATH,str);
    struct stat st;
    stat(buffs, &st);
    free(buffs);
    return st.st_size;
}
static void processDir(const char*path){
    DIR* dir = opendir(path);
    char*buffs=(char*)malloc(300);
    if(dir!=NULL){
        struct stat st;
        do{
            struct dirent* de = readdir(dir);
            if (!de) {
                break;
            }
            if(path[strlen(path)-1]=='/'){
                sprintf(buffs,"%s%s",path,de->d_name);
            }else{
                sprintf(buffs,"%s/%s",path,de->d_name);
            }
            stat(buffs, &st);
            sprintf(buffs,"02-26-24 06:00PM %ld %s\r\n",st.st_size,de->d_name);
            sendData(buffs,strlen(buffs));
        }while(1);
        closedir(dir);
    }
    free(buffs);
}

static void processFile(const char*str){
    char*fileName=(char*)malloc(60);
    char*buffs=(char*)malloc(100);
    int len=0;
    sprintf(fileName,"%s%s",PATH,str);
    FILE*fl=fopen(fileName,"r");
    do{
        len=fread(buffs,1,100,fl);
        if(len>0){
            sendData(buffs,len);
        }
    }while(len>0);
    fclose(fl);
    free(fileName);
    free(buffs);
}
static void writeFile(const char*str){
    char*fileName=(char*)malloc(60);
    int bufSize=100;
    char*buffs=(char*)malloc(bufSize);
    int len=0;
    sprintf(fileName,"%s%s",PATH,str);
    FILE*fl=fopen(fileName,"w");
    do{
        len = recv(processSock, buffs,bufSize, 0);
        if(len>0){
            fwrite(buffs,len,1,fl);
        }
    }while(len>0);
    fclose(fl);
    free(fileName);
    free(buffs);
}

//动态创建一个监听端口
static int initSocket(int port){
    char addr_str[20];
    int addr_family = AF_INET;
    int ip_protocol = 0;
    int keepAlive = 1;
    int keepIdle = KEEPALIVE_IDLE;
    int keepInterval = KEEPALIVE_INTERVAL;
    int keepCount = KEEPALIVE_COUNT;
    struct sockaddr_storage dest_addr;

     if (addr_family == AF_INET) {
        struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
        dest_addr_ip4->sin_family = AF_INET;
        dest_addr_ip4->sin_port = htons(port);
        ip_protocol = IPPROTO_IP;
    }
    int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return -1;
    }
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    ESP_LOGI(TAG, "Socket created");
    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0) {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
        goto CLEAN_UP;
    }
    ESP_LOGI(TAG, "Socket bound, port %d", port);

    err = listen(listen_sock, 1);
    if (err != 0) {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        goto CLEAN_UP;
    }

   
    struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
    socklen_t addr_len = sizeof(source_addr);
    int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
    if (sock < 0) {
        ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
    }

    // Set tcp keepalive option
    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
    setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
    if (source_addr.ss_family == PF_INET) {
        inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
    }

    ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
    //循环处理数据
    //do_retransmit(sock);

    //shutdown(sock, 0);
    //close(sock);

    processSock=sock;
    CLEAN_UP:
    close(listen_sock);
    return processSock;
}


static void do_retransmit(const int sock){
    int len=0;
    char*rx_buffer=(char*)malloc(128);
    char*path=(char*)malloc(150);
    //先发送欢迎消息
    sprintf(rx_buffer,"220 esp32 FTP Service\r\n");
    send(sock,rx_buffer,strlen(rx_buffer),0);

    do {
        len = recv(sock, rx_buffer, 128 - 1, 0);
        if (len < 0) {
            ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
        } else if (len == 0) {
            ESP_LOGW(TAG, "Connection closed");
        } else {
            rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
            ESP_LOGI(TAG, "fd:%dReceived %d bytes: %s",sock, len, rx_buffer);
            if(strstr(rx_buffer,"USER")){
                //用户登录
                sprintf(rx_buffer,"331 Password required.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
            }else if(strstr(rx_buffer,"PASS")){
                // 密码
                sprintf(rx_buffer,"230 User logged in.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
            }else if(strstr(rx_buffer,"FEAT")){
                //支持的文件类型
                sprintf(rx_buffer,"211-Extended features supported:\r\n211 END\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
            }else if(strstr(rx_buffer,"TYPE A")){
                //切换类型
                sprintf(rx_buffer,"200 Type set to A.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
            }else if(strstr(rx_buffer,"TYPE I")){
                sprintf(rx_buffer,"200 Type set to I.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
            }else if(strstr(rx_buffer,"CWD")){
                //当前目录
                if(strstr(rx_buffer,"CWD .")){
                    sprintf(path,"/");
                }else{
                    rx_buffer[len-1]=0;
                    rx_buffer[len-2]=0;
                    char* str=rx_buffer+4;
                    sprintf(path,"%s",str);
                }

                sprintf(rx_buffer,"250 CWD command successful.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
            }else if(strstr(rx_buffer,"PWD")){
                sprintf(rx_buffer,"257 \"%s\" is current directory.\r\n",path);
                send(sock,rx_buffer,strlen(rx_buffer),0);
            }else if(strstr(rx_buffer,"DELE")){
                rx_buffer[len-1]=0;
                rx_buffer[len-2]=0;
                //删除文件
                char* str=rx_buffer+5;
                sprintf(path,"/spiffs%s",str);
                remove(path);
                //
                sprintf(rx_buffer,"250 CWD command successful.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
                

            }else if(strstr(rx_buffer,"PASV")){
                //请求被动连接服务器
                int port=(random()%100)+1024;
                
                sprintf(rx_buffer,"227 Entering Passive Mode (%ld,%ld,%ld,%ld,%d,%d).\r\n",UserIP&0xFF,(UserIP>>8)&0xFF,(UserIP>>16)&0xFF,(UserIP>>24)&0xFF,port/256,port%256);
                send(sock,rx_buffer,strlen(rx_buffer),0);
                initSocket(port);
            }else if(strstr(rx_buffer,"LIST")){
                sprintf(rx_buffer,"125 Data connection already open; Transfer starting.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);

                processDir(PATH);
                closeSock();
                sprintf(rx_buffer,"226 Transfer complete.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
            }else if(strstr(rx_buffer,"SIZE")){
                rx_buffer[len-1]=0;
                rx_buffer[len-2]=0;
                char* temp=rx_buffer+5;
                int size=getFileSize(temp);
                sprintf(rx_buffer,"213 %d\r\n",size);
                send(sock,rx_buffer,strlen(rx_buffer),0);
            }else if(strstr(rx_buffer,"RETR")){
                rx_buffer[len-1]=0;
                rx_buffer[len-2]=0;
                char temp[40]={0};
                strcpy(temp,rx_buffer+5);
                sprintf(rx_buffer,"150 Opening BINARY mode data connection.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
                //发送数据
                processFile(temp);
                closeSock();
                //发送结束命令
                sprintf(rx_buffer,"226 Transfer complete.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
            }else if(strstr(rx_buffer,"STOR")){
                rx_buffer[len-1]=0;
                rx_buffer[len-2]=0;
                char temp[40]={0};
                strcpy(temp,rx_buffer+5);
                sprintf(rx_buffer,"150 Opening data channel for file upload to server of.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
                //读取文件
                writeFile(temp);

                closeSock();
                //发送结束命令
                sprintf(rx_buffer,"226 Successfully transferred.\r\n");
                send(sock,rx_buffer,strlen(rx_buffer),0);
            }

        }
    } while (len > 0);
    free(rx_buffer);
    free(path);
}


void ftp_server_task(void *pvParameters){

    char addr_str[128];
    int addr_family = (int)pvParameters;
    int ip_protocol = 0;
    int keepAlive = 1;
    int keepIdle = KEEPALIVE_IDLE;
    int keepInterval = KEEPALIVE_INTERVAL;
    int keepCount = KEEPALIVE_COUNT;
    struct sockaddr_storage dest_addr;

     if (addr_family == AF_INET) {
        struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
        dest_addr_ip4->sin_family = AF_INET;
        dest_addr_ip4->sin_port = htons(PORT);
        ip_protocol = IPPROTO_IP;
    }
    int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    ESP_LOGI(TAG, "Socket created");
    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0) {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
        goto CLEAN_UP;
    }
    ESP_LOGI(TAG, "Socket bound, port %d", PORT);

    err = listen(listen_sock, 1);
    if (err != 0) {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        goto CLEAN_UP;
    }

    while (1) {
        struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
        socklen_t addr_len = sizeof(source_addr);
        int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
            break;
        }

        // Set tcp keepalive option
        setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
        if (source_addr.ss_family == PF_INET) {
            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
        }

        ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
        //循环处理数据
        do_retransmit(sock);

        shutdown(sock, 0);
        close(sock);
    }

CLEAN_UP:
    close(listen_sock);
    vTaskDelete(NULL);
}